Header menu logo FSharp.Analyzers.SDK

Optimizing Analyzers Using Ignores

Overview

When writing analyzers, we often seen code which looks like this:

[<EditorAnalyzerAttribute "YourAnalyzer">]
let analyzerEditorContext (ctx: EditorContext) =
    handler ctx.TypedTree

The handler code will typically dig into walking the AST/TAST to analyze code and see if the conditions of the current analyzer are being met.

Using Ignore Ranges

We can optimize our analyzer by checking if there are any ignore ranges with a File scope for the current analyzer. This allows us to skip running the analyzer entirely for files which have been marked to ignore all hits from this analyzer. We cannot skip analyzing files which have more granular ignore ranges (like line or region), since we need to walk the tree to see if any hits fall outside of those ranges.

[<EditorAnalyzerAttribute "YourAnalyzer">]
let analyzerEditorContext (ctx: EditorContext) =
    let ignoreRanges = ctx.AnalyzerIgnoreRanges |> Map.tryFind "your code here"
    
    match ignoreRanges with
    | Some ranges -> 
        if ranges |> List.contains File then
            async { return [] }
        else
            handler ctx.TypedTree
    | None -> handler ctx.TypedTree
val analyzerEditorContext: ctx: 'a -> 'b
val ctx: 'a
val analyzerEditorContext: ctx: 'a -> Async<'b list>
val ignoreRanges: obj list option
Multiple items
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val tryFind: key: 'Key -> table: Map<'Key,'T> -> 'T option (requires comparison)
union case Option.Some: Value: 'T -> Option<'T>
val ranges: obj list
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val contains: value: 'T -> source: 'T list -> bool (requires equality)
val async: AsyncBuilder
union case Option.None: Option<'T>

Type something to start searching.